var express = require('express');
var router = express.Router();
const Bizdef = require('../models/Bizdef');
const Bizrela = require('../models/Bizrela');
const Bizinst = require('../models/Bizinst');
const RelationType = require('../models/RelationType');
var cache = require('../utils/cache')
var formidable = require('formidable');
var fs = require('fs');
const mineType = require("mime-types");
var tools = require('../utils/tools')
var CACHE_BIZ_DEF = 'bizdefs'
/**
 * @swagger
 * /cmdb/bizdefs:
 *   get:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 查询所有业务模型
 *     description: 查询所有业务模型
 *     responses:
 *       200:
 *         description: 不分页
 */
router.get('/', function (req, res, next) {
  Bizdef.find({},{icon: 0},(err, docs) => {
    if (err) return tools.handleError(res, err);
    return res.json({ status: 'success', data: docs })
  })
});

/**
 * @swagger
 *
 * /cmdb/bizdefs/page:
 *   get:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 分页查询业务模型
 *     description: 支持标准方式查询。$_foo=bar
 *     responses:
 *       200:
 *         description: 返回标准分页结构
 */
router.get('/page', async function (req, res, next) {
  let result = await Bizdef.page(tools.queryResolver(req.query), req.query.page, req.query.size)
  return res.json({ status: 'success', data: result.docs, pageable: result.pageable })
});

/**
 * @swagger
 *
 * /cmdb/bizdefs:
 *   post:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 创建业务模型
 *     description: 创建业务模型
 *     parameters:
 *       - name: code
 *         description: 模型编码
 *         in: formData
 *         required: true
 *         type: string
 *       - name: name
 *         description: 模型名称
 *         in: formData
 *         required: true
 *         type: string
 *       - name: desc
 *         description: 描述
 *         in: formData
 *         required: false
 *         type: string
 *       - name: attrs
 *         description: 属性定义
 *         in: formData
 *         required: false
 *         type: arrays
 *     responses:
 *       200:
 *         description: status/data结构
 */
router.post('/', function (req, res, next) {
  var { code, name, attrs, desc } = req.body
  if(!attrs) attrs = []
  var data = { code, name, attrs, desc }
  Bizdef.create(data, function (err, doc) {
    if (err) return tools.handleError(res, err);
    return res.json({ status: 'success', data: doc })
  });
});

/**
 * @swagger
 * /cmdb/bizdefs/{id}:
 *   get:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 业务模型详情
 *     description: 业务模型详情
 *     parameters:
 *       - name: id
 *         description: ID或编码
 *         in: path
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: status/data结构
 */
router.get('/:id', async function (req, res, next) {
  var isById = tools.isValidObjectId(req.params.id)
  var query = isById ? { _id: req.params.id } : { code: req.params.id }

  var doc = await Bizdef.findOne(query)
  if (!doc) {
    return tools.handleError(res, '业务模型不存在');
  }

  return res.json({ status: 'success', data: doc })
});

/**
 * @swagger
 * /cmdb/bizdefs/{id}:
 *   put:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 更新业务模型
 *     description: 更新业务模型
 *     parameters:
 *       - name: id
 *         description: ID或编码
 *         in: path
 *         required: true
 *         type: string
 *       - name: code
 *         description: 模型编码
 *         in: formData
 *         required: true
 *         type: string
 *       - name: name
 *         description: 模型名称
 *         in: formData
 *         required: true
 *         type: string
 *       - name: attrs
 *         description: 属性定义
 *         in: formData
 *         required: false
 *         type: arrays
 *       - name: desc
 *         description: 模型描述
 *         in: formData
 *         required: false
 *         type: string
 *     responses:
 *       200:
 *         description: status/data结构
 */
router.put('/:id', async function (req, res, next) {

  var isById = tools.isValidObjectId(req.params.id)
  var query = isById ? { _id: req.params.id } : { code: req.params.id }
  var doc = await Bizdef.findOne(query)
  if (!doc) {
    return tools.handleError(res, '业务模型不存在');
  }

  var { code, name, attrs, desc } = req.body
  if(!attrs) attrs = []
  var data = { code, name, attrs, desc }

  var afterUpdate = await Bizdef.findOneAndUpdate(query, { $set: data }, { new: true });
  return res.json({ status: 'success', data: afterUpdate })
});


/**
 * @swagger
 *
 * /cmdb/bizdefs/{id}:
 *   delete:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 删除业务模型
 *     description: 删除业务模型
 *     parameters:
 *       - name: id
 *         description: ID
 *         in: path
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: 存在关联关系、存在实例类，status=failure
 */
router.delete('/:id', async function (req, res, next) {
  var isById = tools.isValidObjectId(req.params.id)
  var query = isById ? { _id: req.params.id } : { code: req.params.id }

  var doc = await Bizdef.findOne(query)
  if (!doc) {
    return tools.handleError(res, '业务模型不存在');
  }

  const relationCount = await Bizrela.count({ $or: [{ src: doc.id }, { dest: doc.id }] })
  if (relationCount > 0) {
    return tools.handleError(res, '请先删除业务模型的关联关系');
  }

  const bizinstCount = await Bizinst.count({ schemadef: doc.id })
  if (bizinstCount > 0) {
    return tools.handleError(res, '请先删除业务模型实例');
  }

  Bizdef.findOneAndDelete({ _id: doc.id }, function (err) {
    if (err) return tools.handleError(res, err);
    return res.json({ status: 'success' })
  });
});

/**
 * @swagger
 *
 * /cmdb/bizdefs/{id}/icon:
 *   post:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 设置图标
 *     description: 设置图标
 *     parameters:
 *       - name: id
 *         description: ID
 *         in: path
 *         required: true
 *         type: string
 *       - name: file
 *         description: 图片文件
 *         in: formData
 *         required: true
 *         type: file
 *     responses:
 *       200:
 *         description: status/data结构
 */
router.post('/:id/icon', async function (req, res, next) {

  var isById = tools.isValidObjectId(req.params.id)
  var query = isById ? { _id: req.params.id } : { code: req.params.id }
  var doc = await Bizdef.findOne(query)
  if (!doc) {
    return tools.handleError(res, '业务模型不存在');
  }

  let form = new formidable.IncomingForm();
  form.encoding = 'utf-8';
  form.keepExtensions = true;

  form.parse(req, (err, fields, files) => {
    if (err) return tools.handleError(res, err);
    const imgPath = files.file.path;
    const prefix = "data:" + mineType.lookup(imgPath) + ";base64,";
    const icon = fs.readFileSync(imgPath);
    const base64str = Buffer.from(icon, 'binary').toString('base64')

    Bizdef.findByIdAndUpdate(doc._id, { icon: prefix + base64str }, { new: true }, function (err, doc) {
      if (err) return tools.handleError(res, err);
      return res.json({ status: 'success', data: doc })
    });
  })
})

/**
 * @swagger
 *
 * /cmdb/bizdefs/{id}/icon:
 *   get:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 查询模型图标
 *     description: base64
 *     parameters:
 *       - name: id
 *         description: ID或编码
 *         in: path
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: data.url=base64图标
 */
router.get('/:id/icon', async function (req, res, next) {
  var isById = tools.isValidObjectId(req.params.id)
  var query = isById ? { _id: req.params.id } : { code: req.params.id }

  var doc = await Bizdef.findOne(query)
  if (!doc) {
    return tools.handleError(res, '业务模型不存在');
  }

  return res.json({ status: 'success', data: doc.icon })
})

/**
 * @swagger
 *
 * /cmdb/bizdefs/{id}/relations/add:
 *   post:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 添加关联
 *     description: 添加关联。约束有效值onetoone, onetomany, manytomany, manytoone
 *     parameters:
 *       - name: id
 *         description: ID或编码
 *         in: path
 *         required: true
 *         type: string
 *       - name: type
 *         description: 关联类型ID
 *         in: formData
 *         required: true
 *         type: string
 *       - name: src
 *         description: 源业务模型ID
 *         in: formData
 *         required: true
 *         type: string
 *       - name: dest
 *         description: 目标业务模型ID
 *         in: formData
 *         required: true
 *         type: string
 *       - name: restrain
 *         description: 约束
 *         in: formData
 *         required: true
 *         type: string
 *       - name: desc
 *         description: 描述
 *         in: formData
 *         required: false
 *         type: string
 *       - name: attrs
 *         description: 属性定义
 *         in: formData
 *         required: false
 *         type: arrays
 *     responses:
 *       200:
 *         description: status/data结构
 */
router.put('/:id/relations/add', async function (req, res, next) {

  var isById = tools.isValidObjectId(req.params.id)
  var query = isById ? { _id: req.params.id } : { code: req.params.id }

  var doc = await Bizdef.findOne(query)
  if (!doc) {
    return tools.handleError(res, '业务模型不存在');
  }

  var { type, src, dest, restrain, desc, attrs } = req.body
  var typeInst = await RelationType.findById(type)
  var srcInst = await Bizdef.findById(src)
  var destInst = await Bizdef.findById(dest)

  if (!typeInst || !srcInst || !destInst) {
    return tools.handleError(res, '关联类型，或源业务模型，或目标业务模型不存在');
  }

  var data = {
    type: typeInst._id,
    src: srcInst._id,
    dest: destInst._id,
    restrain,
    attrs,
    desc
  }

  // 没有则插入
  await Bizrela.updateOne({ src: srcInst._id, dest: destInst._id }, data, { upsert: true })
  var r = await Bizrela.findOne({ src: srcInst._id, dest: destInst._id })
  // 关系源模型保存关联引用 TODO: 有可能重复
  await Bizdef.findByIdAndUpdate(src, { $push: { relations: r } });
  // 关系目标模型保存关联引用 TODO: 有可能重复
  await Bizdef.findByIdAndUpdate(dest, { $push: { relations: r } });
  res.json({ status: 'success', data: r })
});


/**
 * @swagger
 *
 * /cmdb/bizdefs/{id}/relations/{rid}:
 *   delete:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 删除关联关系
 *     description: 删除关联关系
 *     parameters:
 *       - name: id
 *         description: 业务模型ID或编码
 *         in: path
 *         required: true
 *         type: string
 *       - name: rid
 *         description: 关联关系ID
 *         in: path
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: 存在关联关系实例，status=failure
 */
router.delete('/:id/relations/:rid', async function (req, res, next) {

  const relation = await Bizrela.findById(req.params.rid)
  if (!relation) {
    return tools.handleError(res, '关联关系不存在')
  }

  const relationInstanceCount = await Bizinst.count({ rela: req.params.rid })
  if (relationInstanceCount > 0) {
    return tools.handleError(res, '请先删除关联关系业务实例')
  }

  // 删除业务模型的关联关系引用
  await Bizdef.updateMany({ _id: { $in: [relation.src, relation.dest] } }, { $pull: { relations: { $in: [relation._id] } } })

  Bizrela.findByIdAndDelete(req.params.rid, function (err) {
    if (err) return tools.handleError(res, err);
    return res.json({ status: 'success' })
  });
});


/**
 * @swagger
 *
 * /cmdb/schemas/{id}/relations:
 *   get:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 查询业务模型关联
 *     description: 查询业务模型关联
 *     parameters:
 *       - name: id
 *         description: ID或编码
 *         in: path
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: 查询业务模型关联
 */
router.get('/:id/relations', async function (req, res, next) {

  var id = req.params.id
  var isById = tools.isValidObjectId(id)
  if (!isById) {
    var doc = await Bizdef.findOne({ code: id })
    if (!doc) {
      return tools.handleError(res, '业务模型不存在')
    }
    id = doc._id
  }

  Bizrela.find({ $or: [{ src: id }, { dest: id }] })
    .populate('type')
    .populate({ path: 'src', select: '-versions' })
    .populate({ path: 'dest', select: '-versions' })
    .exec((err, docs) => {
      if (err) return tools.handleError(res, err);
      return res.json({ status: 'success', data: docs })
    })
});

/**
 * @swagger
 *
 * /cmdb/bizdefs/{id}/topo:
 *   get:
 *     tags:
 *       - 业务模型管理（复杂）
 *     summary: 查询业务模型拓扑
 *     description: 查询业务模型拓扑
 *     parameters:
 *       - name: id
 *         description: ID或编码
 *         in: path
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: 查询业务模型拓扑
 */
router.get('/:id/topo', async function (req, res, next) {

  var isById = tools.isValidObjectId(req.params.id)
  var query = isById ? { _id: req.params.id } : { code: req.params.id }

  var bizdef = await Bizdef.findOne(query)
  if (!bizdef) {
    return tools.handleError(res, '业务模型不存在');
  }

  var cached = await cache.exists(CACHE_BIZ_DEF, bizdef.code + '::topo')
  if (cached) {
    var cachedData = await cache.get(CACHE_BIZ_DEF, bizdef.code + '::topo')
    return res.json({ status: 'success', data: cachedData })
  }

  var bizdefId = bizdef._id.toString()
  let icon = bizdef.icon.toString()

  var relations = await Bizrela.find({ $or: [{ src: bizdefId }, { dest: bizdefId }] })
    .populate('type')
    .populate({ path: 'src', select: '-versions' })
    .populate({ path: 'dest', select: '-versions' })

  var nodes = []
  var edges = []
  nodes.push({ id: bizdef.code, _id: bizdef._id, label: bizdef.name, icon: icon ? icon : '' })

  // 只处理模型的直接关联关系
  for (var i = 0; i < relations.length; i++) {
    var r = relations[i];
    var srcId = r.src._id.toString();
    if (bizdefId == srcId) {
      let icon = r.dest.icon
      if(!nodes.some(e => e.id === r.dest.code)){
        nodes.push({ id: r.dest.code, _id: r.dest._id, label: r.dest.name, icon: icon ? icon : '' })
      }
      edges.push({ from: r.src.code, to: r.dest.code, arrows: 'to', label: r.type.srcdesc })
    } else {
      let icon = r.src.icon
      if(!nodes.some(e => e.id === r.src.code)){
        nodes.push({ id: r.src.code, _id: r.src._id, label: r.src.name, icon: icon ? icon : '' });
      }
      edges.push({ from: r.src.code, to: r.dest.code, arrows: 'from', label: r.type.destdesc });
    }
  }

  cache.set(CACHE_BIZ_DEF, bizdef.code + '::topo', { nodes, edges }, 60 * 1000) // 缓存60秒
  return res.json({ status: 'success', data: { nodes, edges } })
});

module.exports = router;
